package com.eden.inject;

import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

public class InjectAnnotationProcessor extends AbstractProcessor {

    private Messager messager;
    private Elements utils;
    private Filer filter;

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> sets = new HashSet<>();
        sets.add(Inject.class.getName());
        return sets;
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        messager = processingEnvironment.getMessager();
        utils = processingEnvironment.getElementUtils();
        filter = processingEnvironment.getFiler();
    }

    private void printInfo(String info) {
        messager.printMessage(Diagnostic.Kind.NOTE, info);
    }

    private void printError(String error) {
        messager.printMessage(Diagnostic.Kind.ERROR, error);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        processInjectAnnotation(roundEnvironment.getElementsAnnotatedWith(Inject.class));
        return true;
    }

    private void processInjectAnnotation(Set<? extends Element> elements) {
        try {
            for (Element element : elements) {
                Inject inject = element.getAnnotation(Inject.class);
                String className = ((TypeElement) element.getEnclosingElement()).getQualifiedName().toString();
                String packageName = className.substring(0, className.lastIndexOf("."));
                String simpleClassName = className.substring(className.lastIndexOf(".") + 1);
                printInfo("package name :" + packageName);
                printInfo("simple Class name :" + simpleClassName);
                String variableName = ((VariableElement) element).getSimpleName().toString();
                int value = inject.id();

                //
                MethodSpec.Builder builder = MethodSpec.methodBuilder("inject")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .returns(void.class)
                        .addParameter(Object.class, "target");
                builder.addCode("((" + simpleClassName + ")target)." + variableName + "=" + value + ";");


                MethodSpec injectMethod = builder.build();

                TypeSpec injectClass = TypeSpec.classBuilder(simpleClassName + "$Bind")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addMethod(injectMethod)
                        .build();

                JavaFile javaFile = JavaFile.builder(packageName, injectClass)
                        .build();

                javaFile.writeTo(filter);
//                printInfo(javaFile.toString());
//                try {
//                    Writer writer = filter.createClassFile(className + "$Bind").openWriter();
//                    try {
//                        PrintWriter pw = new PrintWriter(writer);
//                        pw.print(javaFile.toString());
//                        pw.flush();
//
//                    } finally {
//                        writer.close();
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }

            }

        } catch (Exception e) {
            printError(e.getMessage());
        }

    }

}
